﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once
#include "YccModelBase.h"
#include "IDbObject.h"

namespace gcmp
{
    class IDocument;
    class IParameter;
    class IElement;
    class IElementParentReporter;
    class ICopyContext;
    class ICopyStrategyReporter;
    class ICalculatorCollection;
    class Matrix4d;
    class ElementId;
    class UniIdentity;
}

namespace gap
{
    /// \brief GAP的数据扩展接口
    /// 实现与 gcmp::IExternalData 基本一致
    /// 
    /// 提供额外存储数据的地方，GAP对象可以通过该接口增加数据
    /// 通过报告计算器，可参与关联更新计算
    /// 通过汇报拷贝策略，可以实现拷贝行为
    class YCC_MODEL_BASE_EXPORT IExtraData : public gcmp::IDbObject
    {
        DEFINE_CAST_DERIVED(gap::IExtraData, gcmp::IDbObject)
    public:
        virtual ~IExtraData() {}

    public:
        /// \brief 设置该扩展数据所属Element
        /// \param pOwnerElement 所属Element
        /// \return bool
        virtual bool SetOwnerElement(gcmp::IElement* pOwnerElement) = 0;

        /// \brief 获得该扩展数据所属Element
        /// \return 所属Element
        virtual const gcmp::IElement* GetOwnerElement() const = 0;

        /// \brief 获得该扩展数据所属Element
        /// \return 所属Element
        virtual gcmp::IElement* GetOwnerElement() = 0;

#pragma region 关联更新
        /// \brief 报告依赖的ElementIds，对象删除和关联更新依赖于这个接口的正确实现
        /// \param reporter 报告接口
        /// \return void
        virtual void ReportParents(gcmp::IElementParentReporter& reporter) const = 0;

        /// \brief 当弱依赖的Element被删除后，此接口会被调用
        /// \param deleteElementIds 表示将被删除的Element ids
        /// \return void
        virtual void UpdateForWeakParentDeletion(const std::set<gcmp::ElementId>& deletedElementIds) = 0;

        /// \brief 外部用户报告自己定制的计算器
        /// GetCalculators中需要调用ICalculatorCollection::AddCalculator(OwnerPtr<ICalculator>)将计算器注册到ICalculatorCollection中
        /// 若配合ADD_CALCULATOR宏使用，用户重写此方法时形参名也需使用“calculators”
        /// 
        /// \param calculators 计算器收集器指针
        /// \return void
        virtual void GetCalculators(gcmp::ICalculatorCollection* calculators) const = 0;
#pragma endregion 关联更新

#pragma region 参数
        /// \brief 报告原生参数
        /// \param pParamDefIds Element拥有的原生参数
        /// \return void
        virtual void ReportParameterDefinitions(std::vector<int>* pParamDefIds) const = 0;

        /// \brief 通过参数id来获得原生参数，此参数id文档唯一
        /// \param paramDefId 文档唯一的参数id
        /// \return 返回Parameter对象，如果失败则返回空的OwnerPtr。
        virtual gcmp::OwnerPtr<gcmp::IParameter> GetNativeParameter(int paramDefId) const = 0;

        /// \brief 设置原生参数
        /// 如果Element有原生参数（Native Parameter），该类需定制SetNativeParameter方法
        /// 
        /// \param pParameter 输入要设置的参数对象
        /// \param errorMessage 输出错误信息，为可选参数。失败的情况下获取错误信息
        /// \return bool 返回true表示设置成功，否则表示设置失败。如果失败可以通过errorMessage参数获取错误信息
        virtual bool SetNativeParameter(const gcmp::IParameter* pParameter, std::wstring* errorMsg = nullptr) = 0;

        /// \brief 获取参数是否可以被修改
        /// \param paramDefId 文档唯一的参数id
        /// \return 如果参数可被修改, 返回true, 否则返回false
        virtual bool IsParameterModifiable(int paramDefId) const = 0;

        /// \brief 是否是预设参数
        /// \param paramDefUid 参数定义Uid
        /// \return 返回true表示是预设参数，否则返回false
        virtual bool IsPresetParameter(const gcmp::UniIdentity& paramDefUid) const = 0;
#pragma endregion 参数

#pragma region 拷贝策略
        /// \brief 报告引用的Element的id为拷贝、保持、放弃
        /// \param copyContext 拷贝上下文
        /// \param reporter 拷贝策略报告器
        /// \return void
        /// \see ICopyStrategyReporter
        virtual void Report(const gcmp::ICopyContext& copyContext, gcmp::ICopyStrategyReporter& reporter) const = 0;
#pragma endregion 拷贝策略

#pragma region 拷贝粘贴行为
        /// \brief 复制粘贴后更新操作
        /// 复制粘贴后需要的后处理可以放在这里，比如：对被复制粘贴后的元素进行重新命名
        /// 
        /// \return void
        virtual void UpdateAfterCopy(const gcmp::ICopyContext& copyContext) = 0;

        /// \brief 当Weak Parent被拷贝但此Element没有同时被拷贝时，此函数被调用
        /// 调用时机：当Weak Parent被拷贝但此Element没有同时被拷贝时，此Element会通过此接口收到通知
        /// 用途：一般用来处理Element存在Id容器的情况
        /// 例子：当Sketch里的对象被拷贝但Sketch本身没被拷贝时，Sketch通过实现此接口将拷贝的新对象加到SketchObjectId容器中
        /// 
        /// \param pDocumentFrom 源文档
        /// \param pSource 被拷贝的IElement指针
        /// \param pCopy 拷贝得到的IElement指针
        /// \return void
        virtual void UpdateForWeakParentCopied(const gcmp::IDocument* pDocumentFrom, const gcmp::IElement* pSource, const gcmp::IElement* pCopy) const = 0;

        /// \brief 决定是否需要一起被拷贝
        /// 调用时机：当一个对象是否拷贝不仅仅取决于一个对象时，无法通过Report来直接决定是否拷贝
        /// 用途：根据待拷贝对象集来决定自己是否拷贝
        /// 例子：墙连接取决于连接的两个墙、尺寸约束取决于约束的对象
        /// 
        /// \param copyContext 拷贝上下文
        /// \return 返回true表示需要，false表示不需要
        virtual bool ShouldBeCopiedTogether(const gcmp::ICopyContext& copyContext) const = 0;

        /// \brief 跨文档拷贝时判断StrongParents是否需要被拷贝
        /// 调用时机：跨文档拷贝时判断StrongParents是否需要被拷贝
        ///
        /// \return 返回true表示需要，false表示不需要
        virtual bool IsStrongParentsNeedCopied() const = 0;

        /// \brief 判断是否可以复制
        /// \param pErrorMessage 输出信息
        /// \return 返回true表示可以复制，false表示不能复制
        virtual bool CanBeCopied(std::wstring* pErrorMessage) const = 0;
#pragma endregion 拷贝粘贴行为

    };
} // namespace gap
